package dkbta;

import static dkbta.util.Utils.readDOMDocFromXML;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.w3c.dom.Element;

import dkbta.ontology.Ontology;
import dkbta.processing.ActionTimer;
import dkbta.processing.Patients;
import dkbta.processing.PlanLoader;
import dkbta.processing.UnitPorts;
import dkbta.processing.WorkQueue;
import dkbta.processing.data.DataDispatcher;
import dkbta.processing.data.DataServer;
import dkbta.util.Log;

/**
 * The actual computational unit
 * 
 * @author Uri Kanonov
 */
public class DKBTA {
	private static final boolean COORDINATOR_PRESENT = Config.getBoolean(
			"COORDINATOR_PRESENT", true);

	private volatile boolean m_shouldRun;
	private LinkedBlockingQueue<String> m_plansQueue;
	private ConnectionHandler m_connectionHandler;
	private volatile boolean m_isReady;
	private final int UNIT_ID;
	private static Logger s_logger;
//	private DataDispatcher m_dd;
//	private WorkQueue m_wq;
	private Log log; //original code

	private ActionTimer actionTimer;

	public DKBTA() throws IOException{
		PropertyConfigurator.configure("./files/conf/unit.log4j.properties");
//		m_wq = new WorkQueue();
//		m_dd = new DataDispatcher();
		m_isReady = false;
		m_shouldRun = true;
		m_plansQueue = new LinkedBlockingQueue<String>();
		m_connectionHandler = new ConnectionHandler(m_plansQueue);
		UNIT_ID = m_connectionHandler.getUnitId();
		s_logger = Logger.getLogger(DKBTA.class);	
		log = Log.init("" + UNIT_ID);
	}

	public DKBTA(String s){
		PropertyConfigurator.configure("./files/conf/unit.log4j.properties");
		UNIT_ID = 3;
		s_logger = Logger.getLogger(DKBTA.class);
	}

	public void startProcess() throws IOException {
		m_connectionHandler.start();
		actionTimer = ActionTimer.init(UNIT_ID);
		try{
			while(m_shouldRun){
				s_logger.info("Waiting for a plan");
				String planStr = m_plansQueue.take();
				//				s_logger.info("Plan received");
				s_logger.info("Plan received \n" + planStr);
				String resultStr = process(planStr.substring(planStr.indexOf("#") + 1));
				s_logger.info("Plan processed ." + resultStr + ".");
				m_connectionHandler.sendResults(Config.RESULTS_PREFIX + resultStr);
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	private String process(String planStr)throws IOException  {						
        try {
            		try{
            			// Initializing the global objects
            			log.fine("Started initialization");
            			WorkQueue wq = new WorkQueue();
            			DataDispatcher dd = new DataDispatcher();
            			Patients patients = new Patients(dd);
            			PlanLoader planLoader = new PlanLoader(planStr, patients, dd, wq);
            			Ontology ontology = planLoader.getOntology();
            			UnitPorts ports = planLoader.getPorts(UNIT_ID);
            			DataServer ds = new DataServer(patients, ontology, ports.dataPort.getPort());
            			ds.start();
            			log.fine("Finished initialization");
            			//waitForCoordinator(ports);
            			System.out.println("Unit " + UNIT_ID + " started working");
            			log.fine("Started working");
            //			new CommandThread(wq, patients).start();
            			// Initializing the data of all the patients
            			planLoader.initializePatients();
            			planLoader = null;
            			// Starting the local computation
            			dd.start();
            			wq.work();
            			// Telling the data dispatcher there won't be any further data batches coming
            			// and that it can shut down as soon as it finishes sending the ones it has.
            			// This method will return once the data dispatcher has finished sending
            			// all of its data (and registering the sending times)
            			dd.waitForFinish();
            			ds.waitForFinish();
            			log.fine("Finished working");
            			// Printing a summary of the stored instances to the stdout
            			System.out.println();
            			patients.printInstances();
            			//actionTimer.close();
            		}catch(Throwable t){
            			log.severe("Unit: " + UNIT_ID + " crashed", t);
            			//log.info(wq.toString());
            			System.exit(1);
            		}
            Thread.sleep(3000);
        } catch (InterruptedException ex) {}
		return "Results. unit id #" + UNIT_ID + " query id #-0-";
	}

	private String process2(String planStr)throws IOException  {
		try{
			log.fine("Started initialization");			
			WorkQueue wq = new WorkQueue();			
			DataDispatcher dd = new DataDispatcher();
			Patients patients = new Patients(dd);
			PlanLoader planLoader = new PlanLoader(planStr, patients, dd, wq);
			Ontology ontology = planLoader.getOntology();
			UnitPorts ports = planLoader.getPorts(UNIT_ID);
			DataServer ds = new DataServer(patients, ontology, ports.dataPort.getPort());
			ds.start();
			log.fine("Finished initialization");
			System.out.println("Unit " + UNIT_ID + " started working");
			log.fine("Started working");
//			new CommandThread(wq, patients).start();
			planLoader.initializePatients();
			planLoader = null;
			dd.start();
			wq.work();
			dd.waitForFinish();
			ds.waitForFinish();
			log.fine("Finished working");
			System.out.println();
			patients.printInstances();
		}catch(Throwable t){
			log.severe("Unit: " + UNIT_ID + " crashed", t);
//			log.info(m_wq.toString());
			System.exit(1);
		}
		return "Results. unit id #" + UNIT_ID + " query id #-0-";
	}

	public static void main(String[] args){
		DKBTA unit;
		try {
			unit = new DKBTA();
			unit.startProcess();			
		} 
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//		unit = new DKBTA("");
		//		unit.testStartProcess();
	}

	public void testStartProcess(){
		s_logger.info("Begin start process");
		try {
			String line;
			System.out.println("dir :" + System.getProperty("user.dir"));
			//	        Process p = Runtime.getRuntime().exec("java -jar client.jar");
			Process p = Runtime.getRuntime().exec("java -jar dkbta.jar");
			p.waitFor();
			//	        System.out.println(p.exitValue());
		}
		catch (Exception err) {
			err.printStackTrace();
		}		
		s_logger.info("End start process");
	}

	private String process3(String planStr)throws IOException  {
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
//	private static class CommandThread extends Thread{
//		private final WorkQueue _wq;
//
//		@SuppressWarnings("unused")
//		private final Patients _patients;
//
//		public CommandThread(WorkQueue wq, Patients patients){
//			_wq = wq;
//			_patients = patients;
//			setDaemon(true);
//		}
//
//		@Override
//		public void run(){
//			Log log = Log.getLogger();
//			try{
//				BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//
//				while (true){
//					String line = br.readLine();
//					if ("tasks".equalsIgnoreCase(line)){
//						System.out.println(_wq);
//					}else{
//						System.out.println("Unknown command");
//					}
//				}
//
//			}catch(Throwable t){
//				log.warning("Command thread crashed", t);
//			}
//		}
//	}
}
